package com.yjc.drygoods.product.service.impl;

import com.yjc.common.utils.PageUtils;
import com.yjc.drygoods.product.dao.CategoryDao;
import com.yjc.drygoods.product.entity.CategoryEntity;
import com.yjc.drygoods.product.entity.SpuInfoEntity;
import com.yjc.drygoods.product.feign.WareFeignService;
import com.yjc.drygoods.product.service.CategoryService;
import com.yjc.drygoods.product.service.SpuInfoService;
import com.yjc.drygoods.product.vo.Catelog2Vo;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yjc.common.utils.Query;

import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    RedissonClient redisson;

    @Autowired
    SpuInfoService spuInfoService;

    @Autowired
    WareFeignService wareFeignService;

    public static ThreadLocal<List<SpuInfoEntity>> threadLocal = new ThreadLocal<>();


    /**
     * 查询redis是否存在缓存
     */
    //    public Map<Long, List<Catelog2Vo>> getCategory2And3() {
//
//        ValueOperations<String, String> ops = redisTemplate.opsForValue();
//        //注意这里是catelogJson,不是catalohJson
//        String catelogJson = ops.get("catelogJson");
//        if (StringUtils.isEmpty(catelogJson)) {
//            Map<Long, List<Catelog2Vo>> resultFromDb = getCategory2And3FromDbWithRedisLock();
//            System.out.println("缓存不命中...查询数据库...");
//            return resultFromDb;
//        }
//        Map<Long, List<Catelog2Vo>> resultFromRedis
//                = JSON.parseObject(catelogJson, new TypeReference<Map<Long, List<Catelog2Vo>>>() {
//        });
//        System.out.println("缓存命中...直接返回...");
//        return resultFromRedis;
//    }

    /**
     * 分布式锁
     *
     * @return
     */
    //    public Map<Long, List<Catelog2Vo>> getCategory2And3FromDbWithRedisLock() {
//
//        RLock lock = redisson.getLock("CatalogJson-lock");
//        lock.lock();
//        System.out.println("获取分布式锁成功...");
//        Map<Long, List<Catelog2Vo>> category2And3FromDb = null;
//        try {
//            category2And3FromDb = getCategory2And3FromDb();
//        } catch (Exception e) {
//            e.printStackTrace();
//        } finally {
//            //删除锁
//            lock.unlock();
//        }
//        return category2And3FromDb;
//    }

    /**
     * 从数据库获取数据
     *
     * @return
     */
    @Cacheable(value = {"category"}, key = "#root.methodName", sync = true)
    @Override
    public Map<Long, List<Catelog2Vo>> getCategory2And3() {
        System.out.println("查询数据库...");
        List<CategoryEntity> categorys = this.list(null);
        //1.获取到所有1级分类id
        List<Long> categoryLevel1Ids = categorys.stream()
                .filter(item -> item.getParentCid() == 0L)
                .map(item -> item.getCatId())
                .collect(Collectors.toList());
        Map<Long, List<Catelog2Vo>> catelog1Parent = categoryLevel1Ids.stream().collect(Collectors.toMap(k -> k, l1 -> {
            //2.获取到所有2级分类id
            List<CategoryEntity> childrensCategory2 = getChildrensCategory(categorys, l1);
            List<Catelog2Vo> catelog2Vos = null;
            if (childrensCategory2 != null) {
                catelog2Vos = childrensCategory2.stream().map(category2 -> {
                    Catelog2Vo catelog2Vo
                            = new Catelog2Vo(category2.getParentCid().toString(), null, category2.getCatId().toString(), category2.getName());
                    //3.获取到所有3级分类id
                    List<CategoryEntity> childrensCategory3 = getChildrensCategory(categorys, category2.getCatId());
                    List<Catelog2Vo.Catelog3Vo> catelog3Vos = null;
                    if (childrensCategory3 != null) {
                        catelog3Vos = childrensCategory3.stream().map(category3 -> {
                            Catelog2Vo.Catelog3Vo catelog3Vo
                                    = new Catelog2Vo.Catelog3Vo(category3.getParentCid().toString(), category3.getCatId().toString(), category3.getName());
                            return catelog3Vo;
                        }).collect(Collectors.toList());
                    }
                    catelog2Vo.setCatalog3List(catelog3Vos);
                    return catelog2Vo;
                }).collect(Collectors.toList());
            }
            return catelog2Vos;
        }));
        return catelog1Parent;
    }


    public List<CategoryEntity> getChildrensCategory(List<CategoryEntity> resList, Long parentId) {
        List<CategoryEntity> list = resList.stream()
                .filter(res -> res.getParentCid() == parentId)
                .collect(Collectors.toList());
        return list;
    }

    @Cacheable(value = {"category"}, key = "#root.method.name", sync = true)
    @Override
    public List<CategoryEntity> getCategoryLevel1() {
        QueryWrapper<CategoryEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("parent_cid", 0L);
        return this.list(queryWrapper);
    }


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public List<CategoryEntity> listWithTree() {
        List<CategoryEntity> categoryEntities = baseMapper.selectList(null);
        List<CategoryEntity> res = categoryEntities.stream()
                .filter(e -> e.getParentCid() == 0)
                .sorted((o1, o2) -> o1.getSort() == null ? 0 : o1.getSort() - (o2.getSort() == null ? 0 : o2.getSort()))
                .map(e -> {
                    e.setChildren(getChildren(e, categoryEntities));
                    return e;
                }).collect(Collectors.toList());
        return res;
    }

    public List<CategoryEntity> getChildren(CategoryEntity categoryEntity, List<CategoryEntity> allRes) {
        List<CategoryEntity> collect = allRes.stream()
                .filter(e -> e.getParentCid() == categoryEntity.getCatId())
                .sorted((o1, o2) -> o1.getSort() == null ? 0 : o1.getSort() - (o2.getSort() == null ? 0 : o2.getSort()))
                .map(e -> {
                    e.setChildren(getChildren(e, allRes));
                    return e;
                }).collect(Collectors.toList());
        return collect;
    }

    @Override
    public List<CategoryEntity> listWithSpuTree() {
        List<CategoryEntity> categoryEntities = baseMapper.selectList(null);
        List<SpuInfoEntity> spuInfos = spuInfoService.list();
        //.filter(item -> !wareFeignService.getSingleSpuWare(item.getId()))
        List<SpuInfoEntity> collect = spuInfos.stream().collect(Collectors.toList());
        threadLocal.set(spuInfos);
        List<CategoryEntity> res = categoryEntities.stream()
                .filter(e -> e.getParentCid() == 0)
                .sorted((o1, o2) -> o1.getSort() == null ? 0 : o1.getSort() - (o2.getSort() == null ? 0 : o2.getSort()))
                .map(e -> {
                    e.setChildren(getChildrenWithSpu(e, categoryEntities));
                    return e;
                }).collect(Collectors.toList());
        return res;
    }

    @Override
    public List<CategoryEntity> appListWithSpuTree() {
        List<CategoryEntity> categoryEntities = baseMapper.selectList(null);
        List<SpuInfoEntity> spuInfos = spuInfoService.list();
        threadLocal.set(spuInfos);
        List<CategoryEntity> res = categoryEntities.stream()
                .filter(e -> e.getParentCid() == 0)
                .sorted((o1, o2) -> o1.getSort() == null ? 0 : o1.getSort() - (o2.getSort() == null ? 0 : o2.getSort()))
                .map(e -> {
                    e.setChildren(getChildrenWithSpuAndApp(e, categoryEntities));
                    return e;
                }).collect(Collectors.toList());
        return res;
    }

    public List<CategoryEntity> getChildrenWithSpuAndApp(CategoryEntity categoryEntity, List<CategoryEntity> allRes) {
        List<CategoryEntity> collect = allRes.stream()
                .filter(e -> e.getParentCid() == categoryEntity.getCatId())
                .sorted((o1, o2) -> o1.getSort() == null ? 0 : o1.getSort() - (o2.getSort() == null ? 0 : o2.getSort()))
                .map(e -> {
                    e.setChildren(getChildrenWithSpuAndApp(e, allRes));
                    return e;
                }).collect(Collectors.toList());
        if (collect == null || collect.size() <= 0) {
            List<SpuInfoEntity> spuInfoEntities = threadLocal.get();
            List<CategoryEntity> collect1 = spuInfoEntities.stream()
                    .filter(item -> item.getCatalogId() == categoryEntity.getCatId())
                    .map(item -> {
                        //TODO 下面的仍可改进
                        //此处将spu的id当成了分类的id
                        //将spu的名称当成了分类的名称
                        CategoryEntity cate = new CategoryEntity();
                        //避免与类别id重复，导致app级联重复循环出现
                        cate.setCatId(item.getId() + 10000L);
//                        log.info("cate.getCatId():{}", cate.getCatId());
                        cate.setName(item.getSpuName());
                        return cate;
                    }).collect(Collectors.toList());
            collect = collect1;
        }
        return collect;
    }

    @Override
    public List<Long> findCatelogPathWithSpuId(Long spuId) {
        SpuInfoEntity spuInfo = spuInfoService.getById(spuId);
        Long catalogId = spuInfo.getCatalogId();
        log.info("catalogId:{}", catalogId);
        Long[] catelogPath = this.findCatelogPath(catalogId);
        List<Long> longs = new ArrayList<>(Arrays.asList(catelogPath));
        log.info("longs:{}", longs);
        longs.add(spuId);
        return longs;
    }

    @Override
    public void removeMenuByIds(List<Long> asList) {

        //TODO 2.开始检查被删除菜单是否被其他地方引用
        baseMapper.deleteBatchIds(asList);
    }

    /**
     * 根据传入的分类id，获取该分类完整三级分类id路径
     *
     * @param attrGroupId
     * @return
     */
    @Override
    public Long[] findCatelogPath(Long attrGroupId) {
        if (attrGroupId == null) {
            return null;
        }
        List<CategoryEntity> listRes = new ArrayList<>();
        listRes = findParentCatelogId(attrGroupId, listRes);
        List<Long> list = listRes.stream().map(item -> {
            return item.getCatId();
        }).collect(Collectors.toList());
        Collections.reverse(list);
        return (Long[]) list.toArray(new Long[list.size()]);
    }

    /**
     * 根据传入的分类id，获取该分类完整三级分类名称路径
     *
     * @param attrGroupId
     * @return
     */
    @Override
    public List<String> findCatelogNamePath(Long attrGroupId) {
        if (attrGroupId == null) {
            return null;
        }
        List<CategoryEntity> listRes = new ArrayList<>();
        listRes = findParentCatelogId(attrGroupId, listRes);
        List<String> list = listRes.stream().map(item -> {
            return item.getName();
        }).collect(Collectors.toList());
//        Collections.swap(list, list.indexOf(0), list.indexOf(2));
        Collections.reverse(list);
//        String s = StringUtils.collectionToDelimitedString(list, ",");
        return list;
    }

    //纯字符串不要忘了单引号，key是由Spel解析的

    @Caching(evict = {
            @CacheEvict(value = {"category"}, key = "'getCategory2And3'"),
            @CacheEvict(value = {"category"}, key = "'getCategoryLevel1'")})
    @Transactional
    @Override
    public void updateDetail(CategoryEntity category) {
        this.updateById(category);
//        CategoryBrandRelationEntity categoryBrandRelationEntity = new CategoryBrandRelationEntity();
//        categoryBrandRelationEntity.setCatelogId(category.getCatId());
//        categoryBrandRelationEntity.setCatelogName(category.getName());
//        categoryBrandRelationDao.update
//                (categoryBrandRelationEntity, new UpdateWrapper<CategoryBrandRelationEntity>().eq("catelog_id", category.getCatId()));
    }

    /**
     * 根据传过来的三级分类id，获取完整三级分类链
     *
     * @param id
     * @param list
     * @return
     */
    private List<CategoryEntity> findParentCatelogId(Long id, List<CategoryEntity> list) {
        CategoryEntity byId = this.getById(id);
        list.add(byId);
        if (byId.getParentCid() != 0) {
            findParentCatelogId(byId.getParentCid(), list);
        }
        return list;
    }



    public List<CategoryEntity> getChildrenWithSpu(CategoryEntity categoryEntity, List<CategoryEntity> allRes) {
        List<CategoryEntity> collect = allRes.stream()
                .filter(e -> e.getParentCid() == categoryEntity.getCatId())
                .sorted((o1, o2) -> o1.getSort() == null ? 0 : o1.getSort() - (o2.getSort() == null ? 0 : o2.getSort()))
                .map(e -> {
                    e.setChildren(getChildrenWithSpu(e, allRes));
                    return e;
                }).collect(Collectors.toList());
        if (collect == null || collect.size() <= 0) {
            List<SpuInfoEntity> spuInfoEntities = threadLocal.get();
            List<CategoryEntity> collect1 = spuInfoEntities.stream()
                    .filter(item -> item.getCatalogId() == categoryEntity.getCatId())
                    .map(item -> {
                        //TODO 下面的仍可改进
                        //此处将spu的id当成了分类的id
                        //将spu的名称当成了分类的名称
                        CategoryEntity cate = new CategoryEntity();
                        cate.setCatId(item.getId());
                        cate.setName(item.getSpuName());
                        return cate;
                    }).collect(Collectors.toList());
            collect = collect1;
        }
        return collect;
    }

}